﻿namespace Example.Text.UnitTests
{
    using System;
    using Xunit;

    public class Base36Facts
    {
        [Fact]
        public void ctor()
        {
            Assert.NotNull(new Base36());
        }

        [Fact]
        public void ctor_int()
        {
            Assert.NotNull(new Base36(123));
        }

        [Fact]
        public void ctor_string()
        {
            Assert.NotNull(new Base36("123"));
        }

        [Fact]
        public void ctor_stringEmpty()
        {
            Assert.Throws<ArgumentOutOfRangeException>(() => new Base36(string.Empty));
        }

        [Fact]
        public void ctor_stringInvalid()
        {
            Assert.Throws<FormatException>(() => new Base36("!"));
        }

        [Fact]
        public void ctor_stringNull()
        {
            Assert.Throws<ArgumentNullException>(() => new Base36(null));
        }

        [Fact]
        public void is_IComparable()
        {
            Assert.IsAssignableFrom<IComparable>(new Base36());
        }

        [Fact]
        public void is_ValueType()
        {
            Assert.IsAssignableFrom<ValueType>(new Base36());
        }

        [Fact]
        public void opAddition_Base36_Base36()
        {
            var expected = new Base36(1 + 2);
            var actual = new Base36(1) + new Base36(2);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opAddition_Base36_int()
        {
            var expected = new Base36(1 + 2);
            var actual = new Base36(1) + 2;

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opAddition_int_Base36()
        {
            var expected = new Base36(1 + 2);
            var actual = 1 + new Base36(2);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opDecrement_Base36()
        {
            var expected = new Base36(1);
            var actual = new Base36(2);
            actual--;

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opDivision_Base36_Base36()
        {
            var expected = new Base36(10 / 2);
            var actual = new Base36(10) / new Base36(2);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opDivision_Base36_Base36Zero()
        {
            Assert.Throws<DivideByZeroException>(() => new Base36(10) / new Base36(0));
        }

        [Fact]
        public void opDivision_Base36_int()
        {
            var expected = new Base36(10 / 2);
            var actual = new Base36(10) / 2;

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opDivision_Base36_intZero()
        {
            Assert.Throws<DivideByZeroException>(() => new Base36(10) / 0);
        }

        [Fact]
        public void opDivision_int_Base36()
        {
            var expected = new Base36(10 / 2);
            var actual = 10 / new Base36(2);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opDivision_int_Base36Zero()
        {
            Assert.Throws<DivideByZeroException>(() => 10 / new Base36(0));
        }

        [Fact]
        public void opEquality_Base36_Base36_whenFalse()
        {
            var operand1 = new Base36(2);
            var operand2 = new Base36(1);

            Assert.False(operand1 == operand2);
        }

        [Fact]
        public void opEquality_Base36_Base36_whenSame()
        {
            var operand1 = new Base36();
            var operand2 = operand1;

            Assert.True(operand1 == operand2);
        }

        [Fact]
        public void opEquality_Base36_Base36_whenTrue()
        {
            var operand1 = new Base36();
            var operand2 = new Base36();

            Assert.True(operand1 == operand2);
        }

        [Fact]
        public void opGreater_Base36_Base36_whenFalse()
        {
            var expr1 = new Base36(1);
            var expr2 = new Base36(2);

            Assert.False(expr1 > expr2);
        }

        [Fact]
        public void opGreater_Base36_Base36_whenSame()
        {
            var expr1 = new Base36();
            var expr2 = expr1;

            Assert.False(expr1 > expr2);
        }

        [Fact]
        public void opGreater_Base36_Base36_whenTrue()
        {
            var expr1 = new Base36(2);
            var expr2 = new Base36(1);

            Assert.True(expr1 > expr2);
        }

        [Fact]
        public void opImplicit_Base36_int()
        {
            var expected = new Base36(1234);
            Base36 actual = 1234;

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opImplicit_Base36_string()
        {
            var expected = new Base36("abc");
            Base36 actual = "abc";

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opImplicit_Base36_stringEmpty()
        {
            Assert.Throws<ArgumentOutOfRangeException>(() => (Base36)string.Empty);
        }

        [Fact]
        public void opImplicit_Base36_stringNull()
        {
            Assert.Throws<ArgumentNullException>(() => (Base36)(string)null);
        }

        [Fact]
        public void opImplicit_int_Base36()
        {
            const int expected = 1234;
            var actual = new Base36(1234);

            Assert.Equal<Base36>(expected, actual);
        }

        [Fact]
        public void opImplicit_string_Base36()
        {
            const string expected = "abc";
            var actual = new Base36("abc");

            Assert.Equal<Base36>(expected, actual);
        }

        [Fact]
        public void opIncrement_Base36()
        {
            var expected = new Base36(2);
            var actual = new Base36(1);
            actual++;

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opInequality_Base36_Base36_whenFalse()
        {
            var operand1 = new Base36();
            var operand2 = new Base36();

            Assert.False(operand1 != operand2);
        }

        [Fact]
        public void opInequality_Base36_Base36_whenSame()
        {
            var operand1 = new Base36();
            var operand2 = operand1;

            Assert.False(operand1 != operand2);
        }

        [Fact]
        public void opInequality_Base36_Base36_whenTrue()
        {
            var operand1 = new Base36(2);
            var operand2 = new Base36(1);

            Assert.True(operand1 != operand2);
        }

        [Fact]
        public void opLesser_Base36_Base36_whenFalse()
        {
            var expr1 = new Base36(2);
            var expr2 = new Base36(1);

            Assert.False(expr1 < expr2);
        }

        [Fact]
        public void opLesser_Base36_Base36_whenSame()
        {
            var expr1 = new Base36();
            var expr2 = expr1;

            Assert.False(expr1 < expr2);
        }

        [Fact]
        public void opLesser_Base36_Base36_whenTrue()
        {
            var expr1 = new Base36(1);
            var expr2 = new Base36(2);

            Assert.True(expr1 < expr2);
        }

        [Fact]
        public void opModulus_Base36_Base36()
        {
            var expected = new Base36(10 % 3);
            var actual = new Base36(10) % new Base36(3);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opModulus_Base36_Base36Zero()
        {
            Assert.Throws<DivideByZeroException>(() => new Base36(0) % new Base36(0));
        }

        [Fact]
        public void opModulus_Base36_int()
        {
            var expected = new Base36(10 % 3);
            var actual = new Base36(10) % 3;

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opModulus_Base36_intZero()
        {
            Assert.Throws<DivideByZeroException>(() => new Base36(0) % 0);
        }

        [Fact]
        public void opModulus_int_Base36()
        {
            var expected = new Base36(10 % 3);
            var actual = 10 % new Base36(3);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opModulus_int_Base36Zero()
        {
            Assert.Throws<DivideByZeroException>(() => 10 % new Base36(0));
        }

        [Fact]
        public void opMultiplication_Base36_Base36()
        {
            var expected = new Base36(2 * 5);
            var actual = new Base36(2) * new Base36(5);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opMultiplication_Base36_int()
        {
            var expected = new Base36(2 * 5);
            var actual = new Base36(2) * 5;

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opMultiplication_int_Base36()
        {
            var expected = new Base36(2 * 5);
            var actual = 2 * new Base36(5);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opSubtraction_Base36_Base36()
        {
            var expected = new Base36(10 - 1);
            var actual = new Base36(10) - new Base36(1);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opSubtraction_Base36_int()
        {
            var expected = new Base36(10 - 1);
            var actual = new Base36(10) - 1;

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void opSubtraction_int_Base36()
        {
            var expected = new Base36(10 - 1);
            var actual = 10 - new Base36(1);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Add_Base36()
        {
            var expected = new Base36(1 + 2);
            var actual = new Base36(1).Add(new Base36(2));

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Add_int()
        {
            var expected = new Base36(1 + 2);
            var actual = new Base36(1).Add(2);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Add_string()
        {
            var expected = new Base36(1 + 2);
            var actual = new Base36(1).Add("2");

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_CompareTo_object()
        {
            var obj = new Base36();

            Assert.Equal(0, new Base36().CompareTo(obj));
        }

        [Fact]
        public void op_CompareTo_objectGreater()
        {
            var obj = new Base36(1);

            Assert.True(new Base36(2).CompareTo(obj) > 0);
        }

        [Fact]
        public void op_CompareTo_objectInt32()
        {
            Assert.Throws<ArgumentOutOfRangeException>(() => new Base36().CompareTo(123));
        }

        [Fact]
        public void op_CompareTo_objectLesser()
        {
            var obj = new Base36(2);

            Assert.True(new Base36(1).CompareTo(obj) < 0);
        }

        [Fact]
        public void op_CompareTo_objectNull()
        {
            Assert.True(new Base36().CompareTo(null) > 0);
        }

        [Fact]
        public void op_Compare_Base36Greater_Base36()
        {
            var comparand1 = new Base36(2);
            var comparand2 = new Base36(1);

            Assert.True(Base36.Compare(comparand1, comparand2) > 0);
        }

        [Fact]
        public void op_Compare_Base36Lesser_Base36()
        {
            var comparand1 = new Base36(1);
            var comparand2 = new Base36(2);

            Assert.True(Base36.Compare(comparand1, comparand2) < 0);
        }

        [Fact]
        public void op_Compare_Base36_Base36_whenEqual()
        {
            var comparand1 = new Base36(123);
            var comparand2 = new Base36(123);

            Assert.Equal(0, Base36.Compare(comparand1, comparand2));
        }

        [Fact]
        public void op_Compare_Base36_Base36_whenSame()
        {
            var comparand1 = new Base36();
            var comparand2 = comparand1;

            Assert.Equal(0, Base36.Compare(comparand1, comparand2));
        }

        [Fact]
        public void op_Decrement()
        {
            var expected = new Base36(1);
            var actual = new Base36(2).Decrement();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Divide_Base36()
        {
            var expected = new Base36(10 / 2);
            var actual = new Base36(10).Divide(new Base36(2));

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Divide_Base36Zero()
        {
            Assert.Throws<DivideByZeroException>(() => new Base36(10).Divide(new Base36(0)));
        }

        [Fact]
        public void op_Divide_int()
        {
            var expected = new Base36(10 / 2);
            var actual = new Base36(10).Divide(2);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Divide_intZero()
        {
            Assert.Throws<DivideByZeroException>(() => new Base36(10).Divide(0));
        }

        [Fact]
        public void op_Divide_string()
        {
            var expected = new Base36(10 / 2);
            var actual = new Base36(10).Divide("2");

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Divide_stringZero()
        {
            Assert.Throws<DivideByZeroException>(() => new Base36(10).Divide("0"));
        }

        [Fact]
        public void op_Equals_object()
        {
            var obj = new Base36();

            Assert.True(new Base36().Equals(obj));
        }

        [Fact]
        public void op_Equals_objectNull()
        {
            Assert.False(new Base36().Equals(null));
        }

        [Fact]
        public void op_Equals_objectString()
        {
            Assert.False(new Base36("abc").Equals("abc"));
        }

        [Fact]
        public void op_Equals_object_this()
        {
            var obj = new Base36();

            Assert.True(obj.Equals(obj));
        }

        [Fact]
        public void op_GetHashCode()
        {
            const int expected = 123;
            var actual = new Base36(expected).GetHashCode();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Increment()
        {
            var expected = new Base36(2);
            var actual = new Base36(1).Increment();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Mod_Base36()
        {
            var expected = new Base36(10 % 3);
            var actual = new Base36(10).Mod(new Base36(3));

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Mod_Base36Zero()
        {
            Assert.Throws<DivideByZeroException>(() => new Base36(10).Mod(new Base36(0)));
        }

        [Fact]
        public void op_Mod_int()
        {
            var expected = new Base36(10 % 3);
            var actual = new Base36(10).Mod(3);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Mod_intZero()
        {
            Assert.Throws<DivideByZeroException>(() => new Base36(10).Mod(0));
        }

        [Fact]
        public void op_Mod_string()
        {
            var expected = new Base36(10 % 3);
            var actual = new Base36(10).Mod("3");

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Mod_stringZero()
        {
            Assert.Throws<DivideByZeroException>(() => new Base36(10).Mod("0"));
        }

        [Fact]
        public void op_Multiply_Base36()
        {
            var expected = new Base36(2 * 5);
            var actual = new Base36(2).Multiply(new Base36(5));

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Multiply_int()
        {
            var expected = new Base36(2 * 5);
            var actual = new Base36(2).Multiply(5);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Multiply_string()
        {
            var expected = new Base36(2 * 5);
            var actual = new Base36(2).Multiply("5");

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Subtract_Base36()
        {
            var expected = new Base36(10 - 1);
            var actual = new Base36(10).Subtract(new Base36(1));

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Subtract_int()
        {
            var expected = new Base36(10 - 1);
            var actual = new Base36(10).Subtract(1);

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_Subtract_string()
        {
            var expected = new Base36(10 - 1);
            var actual = new Base36(10).Subtract("1");

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToInt32()
        {
            Assert.Equal(0, new Base36().ToInt32());
        }

        [Fact]
        public void op_ToInt32_when10()
        {
            const int expected = 36;
            var actual = new Base36("10").ToInt32();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToInt32_when100()
        {
            const int expected = 36 * 36;
            var actual = new Base36("100").ToInt32();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToInt32_when123()
        {
            const int expected = 123;
            var actual = new Base36(expected).ToInt32();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToInt32_whenMax()
        {
            const int expected = int.MaxValue;
            var actual = new Base36("zik0zj").ToInt32();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToInt32_whenMin()
        {
            const int expected = int.MinValue + 1;
            var actual = new Base36("-zik0zj").ToInt32();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToInt32_whenZz()
        {
            const int expected = (36 * 36) - 1;
            var actual = new Base36("zz").ToInt32();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToString_0to9()
        {
            for (var i = 0; i < 10; i++)
            {
                var expected = i.ToString();
                var actual = new Base36(i).ToString();

                Assert.Equal(expected, actual);
            }
        }

        [Fact]
        public void op_ToString_10()
        {
            const string expected = "10";
            var actual = new Base36(36).ToString();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToString_100()
        {
            const string expected = "100";
            var actual = new Base36(36 * 36).ToString();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToString_AtoZ()
        {
            for (var i = 10; i < 36; i++)
            {
                var expected = ((char)(i + 87)).ToString();
                var actual = new Base36(i).ToString();

                Assert.Equal(expected, actual);
            }
        }

        [Fact]
        public void op_ToString_Max()
        {
            const string expected = "zik0zj";
            var actual = new Base36(int.MaxValue).ToString();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToString_Min()
        {
            const string expected = "-zik0zj";
            var actual = new Base36(int.MinValue + 1).ToString();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void op_ToString_zz()
        {
            const string expected = "zz";
            var actual = new Base36((36 * 36) - 1).ToString();

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void prop_MaxValue_get()
        {
            Base36 expected = int.MaxValue;
            var actual = Base36.MaxValue;

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void prop_MinValue_get()
        {
            Base36 expected = int.MinValue;
            var actual = Base36.MinValue;

            Assert.Equal(expected, actual);
        }

        [Fact]
        public void prop_Zero_get()
        {
            Base36 expected = 0;
            var actual = Base36.Zero;

            Assert.Equal(expected, actual);
        }
    }
}